package com.enlorenz.common.security;

import java.security.MessageDigest;

import javax.crypto.KeyGenerator;
import javax.crypto.Mac;
import javax.crypto.SecretKey;
import javax.crypto.spec.SecretKeySpec;

import org.apache.commons.codec.binary.Base64;

import com.enlorenz.common.util.CommonContants;
import com.enlorenz.common.util.StringUtils;

/**
 * 基础的加密工具
 * SHA更安全更难破解，运算消耗比MD5也更大
 * 
 *BASE64的加密解密是双向的，可以求反解。 
  MD5、SHA以及HMAC是单向加密，任何数据加密后只会产生唯一的一个加密串，通常用来校验数据在传输过程中是否被修改。
  其中HMAC算法有一个密钥，增强了数据传输过程中的安全性，强化了算法外的不可控因素。 
    单向加密的用途主要是为了校验数据在传输过程中是否被修改。 
 * @author Karma
 *
 */
public class Coder4Base {
	public static final String BASE_CODE=CommonContants.UTF_8_CHARSET;
	public static final String KEY_SHA = "SHA";  
	public static final String KEY_MD5 = "MD5";  
    /** 
     * MAC算法可选以下多种算法 
     */  
    public static enum HmacTypeEnum {
    	HmacMD5,HmacSHA1,HmacSHA256,HmacSHA384,HmacSHA512
    }
    
	/**
	 * BASE64
	 * 按照RFC2045的定义，Base64被定义为：Base64内容传送编码被设计用来把任意序列的8位字节描述为一种不易被人直接识别的形式。（The Base64 Content-Transfer-Encoding is designed to represent arbitrary sequences of octets in a form that need not be humanly readable.） 
常见于邮件、http加密，截取http信息，你就会发现登录操作的用户名、密码字段通过BASE64加密的。 
	主要就是BASE64Encoder、BASE64Decoder两个类，我们只需要知道使用对应的方法即可。另，BASE加密后产生的字节位数是8的倍数，如果不够位数以=符号填充。 
	 */
	/**
	 * BASE64解密
	 * @param data
	 * @return
	 * @throws Exception
	 */
	public static byte[] decryptBASE64(String data){
		return decryptBASE64Inner(data,BASE_CODE);
	}
	
	public static byte[] decryptBASE64Inner(String data,String encode){
		return Base64.decodeBase64(StringUtils.getBytes(data,encode));
	}
	
	public static String decryptBASE64(String data,String encode){
		byte [] result=decryptBASE64Inner(data,encode);
		return StringUtils.newString(result,encode);
	}
	
	public static String decryptBASE64ByUTF8(String data){
		return decryptBASE64(data,BASE_CODE);
	}


	/**
	 * BASE64加密
	 * @param key
	 * @return
	 * @throws Exception
	 */
	public static String encryptBase64(byte[] key,String encode){
		return StringUtils.newString(Base64.encodeBase64(key),encode);
	}
	
	public static String encryptBase64(byte[] key){
		return StringUtils.newString(Base64.encodeBase64(key),BASE_CODE);
	}
	
	public static String encryptBASE64(String key,String encode){
		return encryptBase64(StringUtils.getBytes(key, encode),encode);
	}
	
	public static String encryptBASE64ByUTF8(String key){
		return encryptBASE64(key, BASE_CODE);
	}

	
	/**
	 * MD5 -- message-digest algorithm 5 （信息-摘要算法）缩写，广泛用于加密和解密技术，
	 * 常用于文件校验。校验？不管文件多大，经过MD5后都能生成唯一的MD5值。好比现在的ISO校验，都是MD5校验。怎么用？当然是把ISO经过MD5后产生MD5的值。
	 * 一般下载linux-ISO的朋友都见过下载链接旁边放着MD5的串。就是用来验证文件是否一致的。 
	 */
	   /** 
     * MD5加密 
     *  
     * @param data 
     * @return 
     * @throws Exception 
     */  
    public static byte[] encryptMD5(byte[] data) throws Exception {  
        MessageDigest md5 = MessageDigest.getInstance(KEY_MD5);  
        md5.update(data);  
        return md5.digest();  
    }  
	
	
	/**
	 * SHA(Secure Hash Algorithm，安全散列算法），数字签名等密码学应用中重要的工具，
	 * 被广泛地应用于电子商务等信息安全领域。虽然，SHA与MD5通过碰撞法都被破解了， 
	 * 但是SHA仍然是公认的安全加密算法，较之MD5更为安全。 
	 */
    /** 
     * SHA加密 
     * @param data 
     * @return 
     * @throws Exception 
     */  
    public static byte[] encryptSHA(byte[] data) throws Exception {  
        MessageDigest sha = MessageDigest.getInstance(KEY_SHA);  
        sha.update(data);  
        return sha.digest();  
    }  
    
    
    /**
     * HMAC(Hash Message Authentication Code，散列消息鉴别码，基于密钥的Hash算法的认证协议。
     * 消息鉴别码实现鉴别的原理是，用公开函数和密钥产生一个固定长度的值作为认证标识，用这个标识鉴别消息的完整性。
     * 使用一个密钥生成一个固定大小的小数据块，即MAC，并将其加入到消息中，然后传输。接收方利用与发送方共享的密钥进行鉴别认证等。 
     */
    
    /** 
     * 初始化HMAC密钥 
     * @return 
     * @throws Exception 
     */ 
    public static String initMacKey() throws Exception{
    	return initMacKey(HmacTypeEnum.HmacMD5);
    }
   
    /**
     * 初始化HMAC密钥 
     * @param hmacType hmac类型
     * @return
     * @throws Exception
     */
    public static String initMacKey(HmacTypeEnum hmacType) throws Exception {  
        KeyGenerator keyGenerator = KeyGenerator.getInstance(hmacType.name());  
        SecretKey secretKey = keyGenerator.generateKey();  
        return encryptBase64(secretKey.getEncoded(),CommonContants.UTF_8_CHARSET);  
    }  
    
    /** 
     * HMAC加密 
     * @param data 数据
     * @param key 密钥
     * @return 
     * @throws Exception 
     */
    public static byte[] encryptHMAC(byte[] data, String key) throws Exception{
    	return encryptHMAC(data, key, HmacTypeEnum.HmacMD5);
    }
    
    /** 
     * HMAC加密 
     * @param data 数据
     * @param key 密钥
     * @param hmacType hmac加密类型
     * @return 
     * @throws Exception 
     */  
    public static byte[] encryptHMAC(byte[] data, String key,HmacTypeEnum hmacType) throws Exception {  
        SecretKey secretKey = new SecretKeySpec(decryptBASE64(key), hmacType.name());  
        Mac mac = Mac.getInstance(secretKey.getAlgorithm());  
        mac.init(secretKey);  
        return mac.doFinal(data);  
    }  
}
